home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / scsi driver ƒ / SCSI.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-04-17  |  16.6 KB  |  541 lines  |  [TEXT/KAHL]

  1. /*
  2. File SCSI.c, the SCSI Hard Disk Installation Program
  3. Leo Drizis, March 1989
  4. Only for personal use, commercial use prohibited !!!
  5.  
  6. Compile this file with THINK'S Lightspeed C
  7. and link it with the <stdio>, <strings> and <MacTraps> libraries
  8. */
  9.  
  10. #include <DiskDvr.h>        /* include standard definitions from C compiler */
  11. #include <SCSIMgr.h>
  12. #include <stdio.h>
  13. #include <strings.h>
  14.  
  15. #define DEBUG1                /* change it to DEBUG to get detailed messages */
  16. #define PMAPLEN    9                    /* length of partition map in sectors */
  17. #define MAXPARTS    7                            /* max number od partitions */
  18. #define DRVRLEN    10                            /* length of driver in sectors */
  19. #define BLKSIZE    512                                /* size of sector in bytes */
  20. #define BLIND    FALSE                        /* if we use blind reads/writes */
  21.  
  22. #include "SCSITypes.h"                                    /* read our typedefs */
  23.  
  24. struct {
  25.     modeSHead head;
  26.     blockDesc blD;
  27.     serNumPage SNPage;
  28.     } SNPars;                                    /* serial number parameters */
  29.  
  30. struct {
  31.     modeSHead head;
  32.     blockDesc blD;
  33.     geoPage geom;
  34.     } GPars;                                        /* geometry parameters */
  35.  
  36. struct {
  37.     modeSHead head;
  38.     blockDesc blD;
  39.     fmtPage fmt;
  40.     } FPars;                                        /* format parameters */
  41.  
  42. senseData extSens;
  43. OSErr err;                                            /* global error number */
  44. int stat,message; 
  45. int SCSIid,interl;
  46. lsector lsect;
  47. unsigned int checkSum;                                /* checksum of driver */
  48. long rSize;                                /* accurate size of driver in bytes */
  49. SCSIInstr myTIB[3];
  50. Block0 blk0;
  51. Partition prtt;
  52. long capacity;                                /* size of hard disk in sectors */
  53. inqData inqPars;
  54. char actSerNum[10];                                    /* actual serial number */
  55. int nPart;                        /* number of filesystem partitions minus one */
  56. struct {
  57.     long start;                                            /* starting sector */
  58.     long size;                                /* size of partition in sectors */
  59.     Boolean changed,writeProt,hidden;                                /* flags */
  60.     } Part[7];
  61.  
  62. #define LONGWAIT 12*60*60                        /* formatting wait in ticks */
  63. #define TIMEOUT 60            /* wait time in ticks for normal operations */
  64. #define NoErr 0
  65.  
  66. #include "SCSICmds.c"                        /* read the low-level commands */
  67.  
  68.  
  69. Zero(ptr,howMany)                                /* zeroes the given space */
  70. char *ptr;                                            /* pointer to the space */
  71. int howMany;                                    /* size of bytes to erase */
  72. {
  73. register int i;
  74. for (i=0; i<howMany; i++,ptr++) *ptr = 0;
  75. }
  76.  
  77.  
  78.  
  79. Boolean Yes(messg)                /* asks a yes/no question displaying message */
  80. char *messg;
  81. {
  82. char ans[20];
  83.  
  84. printf("%s [y/n] ? ",messg);
  85. scanf("%s",ans);
  86. return((ans[0] == 'y') || (ans[0] == 'Y'));
  87. }
  88.  
  89.  
  90. int ShowError(messg) /* displays SCSI manager errors together with a message */
  91. char *messg;
  92. {
  93. if (err != 0) {
  94.     printf("Error while %s:\n",messg);
  95.     switch (err) {
  96.         case 2 : printf("%s\n","Communications error (operation timeout)"); break;
  97.         case 3 : printf("%s\n","Arbitration timeout waiting for not BSY"); break;
  98.         case 4 : printf("%s\n","Bad parameter or TIB opcode"); break;
  99.         case 5 : printf("%s\n","SCSI bus not in correct phase for attempted operation"); break;
  100.         case 6 : printf("%s\n","Data compare error"); break;
  101.         case 7 : printf("%s\n","SCSI Manager busy"); break;
  102.         case 8 : printf("%s\n","Attempted operation is out of sequence"); break;
  103.         case 9 : printf("%s\n","CPU bus timeout"); break;
  104.         case 10 : printf("%s\n","SCSI bus was not in Status phase"); break;
  105.         default : printf("%s %d\n","Unknown error code = ",err); break;
  106.         }
  107.     printf("SCSI Status : 0x%X\n",SCSIStat() );
  108.     }
  109. #ifdef DEBUG
  110. else printf("%s ok!\n",messg);
  111. #endif
  112. }
  113.  
  114.  
  115. myComplete(tOut)        /* wait a scsi operation to complete and get status */
  116. long tOut;                                            /* waiting time in ticks */
  117. {
  118. long errSect;                            /* the sector that caused the error */
  119.  
  120. #ifdef DEBUG
  121. printf("Trying to complete...");
  122. #endif
  123. err=SCSIComplete(&stat,&message,tOut);                /* scsi manager routine */
  124. #ifdef DEBUG
  125. printf("stat: %d messg: %d\n",stat,message);
  126. #endif
  127. if (stat == 2) {                /* status 2 means that we must request sense */
  128.     reqSen(&extSens);
  129.     errSect=extSens.LBA_MSB*4096L + extSens.LBA_LSB;    /* calc defect sector */
  130.     printf("Sense key : 0x%X\n",extSens.senseKey);
  131.     printf("Offending sector : %ld\n",errSect);
  132.     printf("Additional sense code : 0x%X\n",extSens.addSenCode);
  133.     }
  134. }
  135.  
  136.  
  137.  
  138. fixBlk0()                                /* writes the boot sector (sector 0) */
  139. {
  140. Zero(&blk0,sizeof(blk0));
  141. blk0.sbSig=0x4552;                                                /* signature */
  142. blk0.sbBlkSize=BLKSIZE;                            /* sector size of this disk */
  143. blk0.sbBlkCount=capacity;                                    /* no of sectors */
  144. blk0.sbDevType=1;
  145. blk0.sbDevId=1;
  146. blk0.sbData=0;
  147. blk0.sbDrvrCount=1;                                /* no of drivers on this disk */
  148. blk0.ddBlock=PMAPLEN+1;                            /* starting block of driver */
  149. blk0.ddSize=(rSize >> 9) + 1;                    /* size of driver in sectors */
  150. blk0.ddType=1;
  151. write(0L,&blk0,BLKSIZE);                            /* write it on sector 0 */
  152. }
  153.  
  154.  
  155. fixPartDr()                            /* write the driver partition description */
  156. {
  157. Zero(&prtt,sizeof(prtt));
  158. prtt.pmSig=0x504D;                                    /* partition signature */
  159. prtt.pmMapBlkCnt=nPart+3;                    /* no of sectors in partition map */
  160. prtt.pmPyPartStart=PMAPLEN+1;                /* starting sector of partition */
  161. prtt.pmPartBlkCnt=DRVRLEN;                    /* size of partition in sectors */
  162. stpcpy(prtt.pmPartName,"Macintosh");                    /* partition name */
  163. stpcpy(prtt.pmParType,"Apple_Driver");                    /* partition type */
  164. prtt.pmDataCnt=prtt.pmPartBlkCnt;
  165. prtt.pmPartStatus=0x7F;                                    /* partition flags */
  166. prtt.pmBootSize=rSize;                                    /* size of driver */
  167. prtt.pmBootCksum=checkSum;                                /* checksum of driver */
  168. stpcpy(prtt.pmProcessor,"68000");                /* processor to execute it */
  169. write(1L,&prtt,BLKSIZE);                            /* write it on sector 1 */
  170. }
  171.  
  172.  
  173. fixPartMap()                            /* writes the partition map partition */
  174. {
  175. Zero(&prtt,sizeof(prtt));
  176. prtt.pmSig=0x504D;
  177. prtt.pmMapBlkCnt=nPart+3;
  178. prtt.pmPyPartStart=1;
  179. prtt.pmPartBlkCnt=PMAPLEN;
  180. stpcpy(prtt.pmPartName,"Apple");
  181. stpcpy(prtt.pmParType,"Apple_Partition_Map");
  182. prtt.pmDataCnt=prtt.pmPartBlkCnt;
  183. prtt.pmPartStatus=0x37;
  184. write(2L,&prtt,BLKSIZE);                            /* write it on sector 2 */
  185. }
  186.  
  187.  
  188. fixPartFS()                    /* writes the filesystem partition descriptions */
  189. {
  190. register long i,j;
  191.  
  192. for (i=0; i<=nPart; i++) {                        /* for all defined partitions */
  193.     Zero(&prtt,sizeof(prtt));
  194.     prtt.pmSig=0x504D;
  195.     prtt.pmMapBlkCnt=nPart+3;
  196.     prtt.pmPyPartStart=Part[i].start;
  197.     prtt.pmPartBlkCnt=Part[i].size;
  198.     stpcpy(prtt.pmPartName,"MacOS");
  199.     stpcpy(prtt.pmParType,"Apple_HFS");
  200.     prtt.pmDataCnt=prtt.pmPartBlkCnt;
  201.     prtt.pmPartStatus=0xB7;
  202.     if (Part[i].hidden) prtt.pmPartStatus &= 0xEF; /* mark it as unreadable */
  203.     if (Part[i].writeProt) prtt.pmPartStatus &= 0xDF; /* mark as write protected */
  204.     write(i+3,&prtt,BLKSIZE);
  205.     if (Part[i].changed == TRUE) { /* this partition's size/position has changed */
  206.         printf("Partition %ld is being erased...\n",i+1);
  207.         for (j=0; j<40; j++) {            /* kill the directory writing zeros */
  208.             Zero(lsect,sizeof(lsect));
  209.             write(Part[i].start+2+j,lsect,BLKSIZE);
  210.             }
  211.         }
  212.     }
  213. }
  214.  
  215.  
  216.  
  217. ShowSectHex(ptr,howMany)        /* shows a buffer on the screen for debugging */
  218. unsigned char *ptr;                                            /* ptr to buffer */
  219. int howMany;                                        /* no of bytes to show */
  220. {
  221. int i;
  222.  
  223. for (i=1; i<=howMany; i++,ptr++) {
  224.     printf("%2X ", *ptr);
  225.     if ( i % 16 == 0 ) putch( '\n');
  226.     }
  227. putch( '\n');
  228. }
  229.  
  230.  
  231. ShowSect(ptr,howMany)                        /* as before, but in ascii form */
  232. char *ptr;
  233. int howMany;
  234. {
  235. int i;
  236.  
  237. for (i=1; i<=howMany; i++,ptr++) {
  238.     if ( *ptr > 31 ) putch( *ptr);
  239.     else putch( '.');
  240.     if ( i % 64 == 0 ) putch( '\n');
  241.     }
  242. }
  243.  
  244.  
  245. unsigned int calcChk(ptr,howMany)            /* calculate checksum of driver */
  246. unsigned char *ptr;
  247. long howMany;
  248. {
  249. unsigned int CSum,i;
  250.  
  251. for (i=0,CSum=0; i<howMany; i++,ptr++) {        /* this is apple's algorithm */
  252.     CSum = CSum + *ptr;
  253.     asm { rol.w CSum }
  254.     }
  255. if (CSum == 0) CSum = 0xFFFF;
  256. return(CSum);
  257. }
  258.  
  259.  
  260.  
  261. InstallDriver()            /* writes the driver and all boot info on the disk */
  262. {
  263. Handle hdl;                                                /* handle to driver */
  264. Ptr rPtr;                                                /* pointer to driver */
  265. long bNum;                                                /* block in driver */
  266. int fRef;                                /* file reference no of driver file */
  267. char ans[20];
  268. register int i;
  269.  
  270. printf("Do you really want to install driver & partitions at SCSI ID = %d [y/n] ? ",SCSIid);
  271. scanf("%s",ans);
  272. if (ans[0] != 'y' && ans[0] != 'Y') return;
  273. fRef=OpenResFile("\pDriver.scsi");                        /* get driver file */
  274. hdl=GetResource('scsi',1);                            /* get driver resource */
  275. if (fRef == -1 || hdl == 0) {
  276.     printf("Sorry, cannot find the driver resource !\n");
  277.     err = -1;
  278.     goto exit;
  279.     }
  280. rSize=SizeResource(hdl);                        /* get size of this driver */
  281. printf("Size of driver %ld bytes.\n",rSize);
  282. printf("Installing driver...\n");
  283. HLock(hdl);                        /* lock this handle before dereferencing it */
  284. rPtr= *hdl;
  285. checkSum=calcChk(rPtr,rSize);                            /* find the checksum */
  286. fixBlk0(); if (err != 0) goto exit;                    /* write the boot block */
  287. fixPartMap(); if (err != 0) goto exit;        /* write partition map description */
  288. fixPartDr(); if (err != 0) goto exit;            /* write driver description */
  289. fixPartFS(); if (err != 0) goto exit;                /* write the partitions */
  290. for (i=0, bNum=PMAPLEN+1;        /* write driver on the disk sector for sector */
  291.                 (i <= rSize && bNum < DRVRLEN+PMAPLEN+1);
  292.                 i = i+BLKSIZE, bNum++) {
  293.     write(bNum, (Ptr)((long)rPtr+i),BLKSIZE);
  294.     if (err != 0) goto exit;
  295.     }
  296. CloseResFile(fRef);                        /* we do not need the file any more */
  297. exit:
  298. if (err == 0) printf("Driver now installed, please reboot immediately.\n");
  299. else printf("Sorry, installing failed !\n");
  300. }
  301.  
  302.  
  303. char *GetSerNum()                    /* finds the serial number of the drive */
  304. {
  305. register int i;
  306.  
  307. Zero(&actSerNum,sizeof(actSerNum));
  308. Zero(&inqPars,sizeof(inqPars));
  309. inquiry(&inqPars,sizeof(inqPars)-1);
  310. for (i=0; i<9; i++) actSerNum[i] = inqPars.serNum[i];        /* copy it */
  311. return(actSerNum);
  312. }
  313.  
  314.  
  315. ShowInfo()                    /* reads and displays all drive characteristics */
  316. {
  317. register int i;
  318.  
  319. Zero(&FPars,sizeof(FPars));
  320. modeSen(&FPars,0x3,sizeof(FPars));                /* read format parameter page */
  321. if (err == 0) {
  322.     printf("Number of Sectors : %ld\n",FPars.blD.nBlocks);
  323.     printf("Sector Length : %ld bytes\n",FPars.blD.blkLen);
  324.     printf("Tracks per Cylinder : %d\n",FPars.fmt.trPZone);
  325.     printf("Spare Sectors per Cylinder : %d\n",FPars.fmt.altTrPZone);
  326.     printf("Spare Tracks per Volume : %d\n",FPars.fmt.altTrPVol);
  327.     printf("Sectors per Track : %d\n",FPars.fmt.secPTr);
  328.     printf("Interleave : %d\n",FPars.fmt.iLeave);
  329.     }
  330. Zero(&GPars,sizeof(GPars));
  331. modeSen(&GPars,0x4,sizeof(GPars));                        /* read geometry page */
  332. if (err == 0) {
  333.     printf("Number of Cylinders : %ld\n",GPars.geom.nCyl);
  334.     printf("Number of Heads : %ld\n",GPars.geom.nHead);
  335.     }
  336. Zero(&inqPars,sizeof(inqPars));
  337. inquiry(&inqPars,sizeof(inqPars)-1);                    /* read other parameters */
  338. if (err == 0) {
  339.     printf("Revision level : %d\n",inqPars.revis);
  340.     printf("Manufacturer : '");
  341.     for (i=0; i<8; i++) putch(inqPars.manuf[i]);
  342.     printf("'\n");
  343.     printf("Model : '",inqPars.prod);
  344.     for (i=0; i<16; i++) putch(inqPars.prod[i]);
  345.     printf("'\n");
  346.     printf("Serial Number : '%s'\n",GetSerNum());
  347.     printf("Hardware revision level : %d\n",inqPars.hardRev);
  348.     printf("Firmware revision level : %d\n",inqPars.firmRev);
  349.     printf("ROM revision level : %d\n",inqPars.ROMRev);
  350.     }
  351. ShowDefs();                                        /* count the defect sectors */
  352. }
  353.  
  354.  
  355. ShowDefs()                            /* counts the defect sectors on the disk */
  356. {
  357. unsigned long defects[512];                                    /* defects table */
  358. int defCnt,blCnt;
  359. Boolean nuDefs;                        /* if we have found user defined defects */
  360. register int i;
  361.  
  362. Zero(defects,sizeof(defects));
  363. ReadDefect(defects,sizeof(defects));            /* issue read defect command */
  364. if (err != 0) return;
  365. nuDefs=FALSE;
  366. blCnt=(defects[0]&0x0000FFFF)>>2;    /* calculate no of entries in def. list */
  367. for (i=1,defCnt=0; i <= blCnt; i++) {
  368.     if (defects[i] == 0xFFFFFFFF) { /* this is the start of user defined defects */
  369.         nuDefs=TRUE;
  370.         continue;
  371.         }
  372.     defCnt++;
  373.     if (nuDefs) i++;            /* usr defects are double entries, so adjust */
  374.     }
  375. printf("Number of KNOWN (and set aside) bad sectors: %d\n",defCnt);
  376. }
  377.  
  378.  
  379. my_Menu(menu,item)                                /* handle menu selections */
  380.                 /* THIS FUNCTION IS CALLED FROM stdio'S StdEvent() FUNCTION */
  381. short menu,item;
  382. {
  383. if (menu == 4) {                                            /* SCSI menu */
  384.     printf("\f");
  385.     switch (item) {
  386.         case 1:
  387.             SearchDrives();
  388.             break;
  389.         case 2:
  390.             EnterID();
  391.             break;
  392.         case 3:
  393.             Format();
  394.             break;
  395.         case 4:
  396.             PartDefine();
  397.             break;
  398.         case 5:
  399.             InstallDriver();
  400.             break;
  401.         case 6:
  402.             ShowInfo();
  403.             break;
  404.         }
  405.     printf("Done !\n");
  406.     }
  407. }
  408.  
  409.  
  410.  
  411. SearchDrives()                                /* looks for connected drives */
  412. {
  413. int oldID,nDrives,lastID;
  414.  
  415. oldID=SCSIid;                                            /* save current id */
  416. printf("Searching for connected drives...\n");
  417. for (SCSIid=0,nDrives=0; SCSIid<7; SCSIid++) {
  418.     capacity=Capacity();                        /* get capacity of this drive */
  419.     if (capacity > 0) {                            /* if this drive exists... */
  420.         nDrives++;                                    /* one more drive found */
  421.         lastID=SCSIid;                                /* save last drive found */
  422.         printf("At SCSI ID %d, capacity %ld kBytes.\n",SCSIid,capacity>>1);
  423.         }
  424.     }
  425. if (nDrives == 0) printf("No drives connected !\n");
  426. if (oldID == -1) SCSIid=lastID;                        /* id is last drive found */
  427. else SCSIid=oldID;                                    /* id remains the same */
  428. }
  429.  
  430.  
  431.  
  432. EnterID()                                    /* asks the user for an scsi id */
  433. {
  434. int newID;
  435.  
  436. do {
  437.     printf("Enter the SCSI ID number (now %d), or -1 for no change : ",SCSIid);
  438.     scanf("%d",&newID);
  439.     if (newID == -1) break;
  440.     SCSIid = newID;
  441.     capacity=Capacity();                            /* get drive's size */
  442.     nPart = 0;                                /* one partition is default */
  443.     Zero(Part, sizeof(Part));
  444.     Part[0].start = DRVRLEN+PMAPLEN+1;        /* default start after part. map */
  445.     Part[0].changed = TRUE;                        /* must zero this partition */
  446.     Part[0].hidden = FALSE;
  447.     Part[0].writeProt = FALSE;
  448.     Part[0].size = capacity - (DRVRLEN+PMAPLEN+1);
  449.                                                 /* default size is all disk */
  450.     if (capacity == 0) printf("No such drive connected !\n");
  451.     else printf("This is a %ld kByte hard Disk.\n",capacity>>1);
  452.     }
  453. while (capacity == 0 || !Yes("\nIs the SCSI ID correct")); /* until correct */
  454. printf("SCSI ID is now %d.\n",SCSIid);
  455. }
  456.  
  457.  
  458.  
  459. PartDefine()                        /* define all partitions' characteristics */
  460. {
  461. long i;
  462. long currFree,curr1Free,currSize;
  463.  
  464. printf("Current Partition Info:\n");
  465. for (i=0; i<=nPart; i++) {                                /* for all partitions */
  466.     read(i+3,&prtt,BLKSIZE);            /* read part. description from disk */
  467.     if (prtt.pmSig == 0x504D && strcmp(prtt.pmParType,"Apple_HFS") == 0 ) {
  468.                                         /* we found a filesystem partition */
  469.         nPart = prtt.pmMapBlkCnt - 3;            /* get the real no of parts. */
  470.         Part[i].start = prtt.pmPyPartStart;
  471.         Part[i].size = prtt.pmDataCnt;
  472.         Part[i].changed = FALSE;
  473.         Part[i].hidden = ((prtt.pmPartStatus & 0x0010) == 0);
  474.         Part[i].writeProt = ((prtt.pmPartStatus & 0x0020) == 0);
  475.         }
  476.     printf("No %ld, Starting Sector : %ld, size : ",i+1,Part[i].start);
  477.     printf("%ld kBytes",Part[i].size>>1);
  478.     if (Part[i].hidden) printf(" Hidden");
  479.     if (Part[i].writeProt) printf(" Protected");
  480.     printf("\n");
  481.     }
  482. if (Yes("Do you want to change the Partition info")) {
  483.     curr1Free = DRVRLEN+PMAPLEN+1;                /* actual first free sector */
  484.     currFree = capacity - curr1Free;                    /* actual free size */
  485.     printf("Enter new number of Partitions (max 7) : ");
  486.     scanf("%d",&nPart); nPart--;                                /* minus one */
  487.     for (i=0; i<=nPart && i<=MAXPARTS; i++) {
  488.         do {
  489.             printf("Partition No. %ld, %ld kBytes free.\n",i+1,currFree>>1);
  490.             printf("Enter size of this Partition in kBytes: ");
  491.             scanf("%ld",&currSize);
  492.             currSize <<= 1;                                /* make it in sectors */
  493.             if (currFree<currSize) printf("Not so much place free !\n");
  494.             if (currSize < 1024) printf("Partitions cannot be so small !\n");
  495.             }
  496.         while (currFree<currSize && currSize<1024);            /* until correct */
  497.         Part[i].hidden = Yes("Is this Partition hidden");
  498.         Part[i].writeProt = Yes("Is this Partition write protected");
  499.         if (Part[i].start != curr1Free || Part[i].size != currSize) {
  500.                                 /* if size or position of this part. changed */
  501.             Part[i].start = curr1Free;
  502.             Part[i].size = currSize;
  503.             Part[i].changed = TRUE;                    /* mark it for erasing */
  504.             }
  505.         curr1Free += currSize;                /* adjust 1st free for next part. */
  506.         currFree -= currSize;                /* adjust free size for next part. */
  507.         if (currFree <= 0) { nPart = i; break; }        /* no free size left */
  508.         }
  509.     }
  510. }
  511.         
  512.  
  513. main()
  514. {
  515. register MenuHandle menu;                            /* handle for our menus */
  516.  
  517. SCSIid = -1;
  518. printf("\f");                /* has the side effect of initialising the stdio ! */
  519. printf("\nHard Disk Utility Program, v1.00\n");
  520. printf("\nCopyright Leo Drizis, March '89\n\n");
  521. menu = NewMenu(4, "\pSCSI");                            /* get a new menu */
  522. AppendMenu(menu, "\pSearch Drives;Change Drive;Format;Partitions;Install;Disk Info;");
  523.                                                         /* add entries to menu */
  524. InsertMenu(menu, 0);                    /* put it together with other menus */
  525. DrawMenuBar();                                            /* make menu visible */
  526. /* err=SCSIReset();                         for the 1st time init the scsi bus */
  527. ShowError("resetting");
  528. SearchDrives();                            /* for the 1st time search for drives */
  529. EnterID();                                    /* for the 1st time ask for scsi id */
  530. printf("Done !\n");
  531. for (;;) {                                /* a small event loop to handle events */
  532. EventRecord event;
  533.     while (! GetNextEvent(everyEvent, &event)) {        /* no events, wait */
  534.         HiliteMenu(0);
  535.         SystemTask();                    /* allow background tasks to continue */
  536.         }
  537.     SetCursor(&arrow);                                /* reset cursor to arrow */
  538.     StdEvent(&event);                            /* call stdio's event handler */
  539.     }
  540. }
  541.